import { GameConfig } from '../configs/GameConfig';
import { Constants } from "../etc/Constants";
import { UnitStage } from '../etc/UnitStage';
import { util } from "../etc/Util";
import GameModel from '../model/GameModel';
import GameStatus from '../model/GameModel';
import { EnumBoutState } from '../modules/BoutModule';
import { PlayerModuleC } from '../modules/PlayerModule';
import {
    AnimationSheet,
    AreaSheet,
    EffectSheet,
    EnumSpawnAroundBuffSheet,
    Motion,
    MotionID,
    SlotChange,
    SoundSheet,
    SpawnAroundBuffSheet
} from "./MotionLibrary";
import { MotionRegister } from "./MotionModule";

// 行为发起方及目标
export class MotionContext {
    from: number
    to?: number
    constructor(from: number, to?: number) {
        this.from = from;
        this.to = to;
    }
}

// 行为基类(处理帧数据)
export abstract class AbstractMotion {
    // motion影响者
    performer: Character

    stage: boolean;
    motionData: Motion;
    currentFrame: number;
    motionContext: MotionContext;

    playedEffectID: number[]

    protected constructor(motionSheets: Motion) {
        this.motionData = motionSheets;
        this.stage = false;
        this.currentFrame = 0;
    }
    get isOwner(): boolean {
        return Player.localPlayer.playerId == this.motionContext.from;
    }

    invoke(performer: Character) {
        this.performer = performer
    }

    finish() {
        if (this.playedEffectID) {
            for (const playedID of this.playedEffectID) {
                EffectService.stop(playedID)
            }
            this.playedEffectID = undefined
        }
    }

    updateLogic() {
        // 无信息
        if (!this.motionData.sheets[this.currentFrame]) return;

        let sheet = this.motionData.sheets[this.currentFrame];
        // 动画
        if (sheet.animation) {
            this.playAnimationByID(sheet.animation);
        }
        // 特效 
        if (sheet.effect) {
            this.playEffectByID(sheet.effect);
        }
        // 音效
        if (sheet.sound) {
            this.playSoundByID(sheet.sound);
        }

        // 自身
        if (this.isOwner) {
            // 瞬闪
            if (sheet.flashRange) {
                this.flash()
            }
            // 给球上buff
            if (sheet.spawnAroundBuff) {
                this.requestAddBallBuff(sheet.spawnAroundBuff)
            }
            // 武器位置变更
            if (sheet.slotChange) {
                this.readCurrentFrameSlotChangeInfo(sheet.slotChange)
            }
        }
    }

    updatePerformant() {
        if (!this.isOwner) return;

        if (!this.motionData.sheets[this.currentFrame]) return;

        let sheet = this.motionData.sheets[this.currentFrame];

        if (!sheet.displacement) return;

        // 防止客户端帧率巨低导致冲刺距离无限大，限制帧率补偿最多3倍
        let lpBalance = Math.min(Constants.LPBalance(), Constants.ClientDelayCompensation)
        let x = sheet.displacement[0] * lpBalance
        let y = sheet.displacement[1] * lpBalance
        let z = sheet.displacement[2] * lpBalance

        let playerDir = this.performer.worldTransform.rotation.z * 0.017453;

        let displacement = new mw.Vector(
            x * Math.cos(playerDir) + y * Math.sin(playerDir),
            x * Math.sin(playerDir) - y * Math.cos(playerDir),
            z
        );

        // 腰部射线
        let middlePlayerPos = this.performer.worldTransform.position.clone();
        let middleEndPoint = mw.Vector.add(middlePlayerPos, displacement)
        let middleHitResult = QueryUtil.lineTrace(this.performer.worldTransform.position, middleEndPoint, true);

        let boundingHeight = this.performer.getBoundingBoxExtent().z

        // 脚部射线
        let downPlayerPos = this.performer.worldTransform.position;
        downPlayerPos.z -= boundingHeight / 3;
        let downEndPoint = downPlayerPos.clone().add(displacement);
        let downHitResult = QueryUtil.lineTrace(downPlayerPos, downEndPoint, true);

        // 脚部射线
        let upPlayerPos = this.performer.worldTransform.position.clone()
        upPlayerPos.z += boundingHeight / 3;
        let upEndPoint = mw.Vector.add(upPlayerPos, displacement)
        let upHitResult = QueryUtil.lineTrace(upPlayerPos, upEndPoint, true);

        // 腰前方有碰撞
        for (let hit of middleHitResult) {
            if (hit.gameObject == this.performer) continue;
            if (hit.gameObject instanceof mw.Trigger) continue;
            if (hit.gameObject instanceof mw.UIWidget) continue;
            // console.error('腰碰撞');
            return;
        }
        // 脚前方有碰撞
        for (let hit of downHitResult) {
            if (hit.gameObject == this.performer) continue;
            if (hit.gameObject instanceof mw.Trigger) continue;
            if (hit.gameObject instanceof mw.UIWidget) continue;
            // console.error('脚碰撞');/
            return;
        }
        // 脑袋前方有碰撞
        for (let hit of upHitResult) {
            if (hit.gameObject == this.performer) continue;
            if (hit.gameObject instanceof mw.Trigger) continue;
            if (hit.gameObject instanceof mw.UIWidget) continue;
            // console.error('脑袋碰撞');
            return;
        }

        displacement.x = Math.round(displacement.x)
        displacement.y = Math.round(displacement.y)
        displacement.z = Math.round(displacement.z)

        this.performer.worldTransform.position = this.performer.worldTransform.position.clone().add(displacement);
    }


    // 给球添加buff
    protected requestAddBallBuff(buffSheet: SpawnAroundBuffSheet) {

    }

    // 瞬闪到球的位置
    protected flash() {

    }

    // 动画
    protected playAnimationByID(anim: AnimationSheet) {
        if (!this.performer || !this.performer.isReady) return;
        let animation = this.performer.loadAnimation(anim.guid)
        if (animation.length != 0) {
            animation.speed = animation.length / (anim.count * Constants.LogicFrameInterval)
        }
        animation.play()
    }

    // 槽位变更
    protected readCurrentFrameSlotChangeInfo(slotInfo: SlotChange) {

    }

    // 特效
    protected playEffectByID(effSheet: EffectSheet) {

        let effectID: number
        // 原地播放特效
        if (effSheet.socketType == -1) {
            let position = this.performer.worldTransform.position.clone()
            // 投影特效
            if (effSheet.projection) {
                let endPosition = this.performer.worldTransform.position.clone()
                endPosition.z -= 1000
                let landChecker = QueryUtil.lineTrace(this.performer.worldTransform.position, endPosition, true)
                let landIndex = landChecker.findIndex(result => {
                    if (result.gameObject == this.performer) return false;
                    if (result.gameObject instanceof Trigger) return false;
                    if (result.gameObject instanceof UIWidget) return false;
                    return true
                })
                if (landIndex != -1) {
                    position.z = landChecker[landIndex].position.z
                }
            }
            let worldOffset = util.vector2World(effSheet.offset, position, this.performer.worldTransform.getForwardVector(), this.performer.worldTransform.getRightVector())
            effectID = EffectService.playAtPosition(effSheet.guid, worldOffset, {
                rotation: effSheet.rotation,
                scale: effSheet.scale
            })
        }
        // 单位身上播放
        else {
            effectID = EffectService.playOnGameObject(effSheet.guid, this.performer, {
                slotType: effSheet.socketType ?? mw.HumanoidSlotType.Root,
                position: effSheet.offset,
                rotation: effSheet.rotation,
                scale: effSheet.scale
            })
        }
        // 储存特效以免motion结束特效不消失
        if (effectID) {
            this.playedEffectID = this.playedEffectID || []
            this.playedEffectID.push(effectID)
        }
        // 设置颜色
        if (effSheet.color && effectID) {
            EffectService.getEffectById(effectID).then(effectObject => {
                if (!effectObject) return
                effectObject.setColor('Color', effSheet.color)
            })
        }
    }

    // 音效
    protected playSoundByID(sound: SoundSheet) {
        mw.SoundService.play3DSound(sound.guid, this.performer.worldTransform.position, 1, sound.volume);
    }
}

// 原始帧数据的实例基类
export class RowMotion extends AbstractMotion {
    constructor(motionSheets: Motion) { super(motionSheets); }
}

// 自定义行为基类(处理帧回调)
export abstract class ExtendsMotion extends AbstractMotion {
    protected constructor(motionSheets: Motion) {
        super(motionSheets);
    }

    invoke(performer: Character) {
        super.invoke(performer);
        if (this.isOwner) {
            this.onOwnerInvoke();
        }
        this.onInvoke();
    }

    updateLogic() {
        super.updateLogic();
        if (this.isOwner) {
            this.onOwnerUpdateLogic();
        }
        this.onUpdateLogic();
    }

    updatePerformant() {
        super.updatePerformant();
        if (this.isOwner) {
            this.onOwnerPerformantUpdate();
        }
        this.onPerformantUpdate();
    }

    finish() {
        super.finish();
        if (this.isOwner) {
            this.onOwnerFinish();
        }
        this.onFinish();
    }

    // 开始
    protected onInvoke() { }
    protected onOwnerInvoke() { }
    // 逻辑帧
    protected onUpdateLogic() { }
    protected onOwnerUpdateLogic() { }
    // 渲染帧
    protected onPerformantUpdate() { }
    protected onOwnerPerformantUpdate() { }
    // 结束
    protected onFinish() { }
    protected onOwnerFinish() { }
}

export class ServerMotion {
    ownerCharacterSceneID: number
    stage: boolean
    motionData: Motion
    currentFrame: number

    ownerCharacter: Character

    constructor(motion: Motion) {
        this.motionData = motion;
    }

    invoke(ownerCharacterSceneID: number) {
        this.ownerCharacterSceneID = ownerCharacterSceneID;
        this.ownerCharacter = GameModel.instance.getCharacterBySceneID(this.ownerCharacterSceneID)
        // 无角色则立即中断motion
        if (!this.ownerCharacter?.isReady) {
            this.currentFrame = 9999
        }
    }

    updateLogic() {
        let sheet = this.motionData.sheets[this.currentFrame];
        if (!sheet) return;

        if (sheet.area) {
            this.areaCheck(sheet.area)
        }
    }

    private areaCheck(area: AreaSheet) {
        let resultArray = UnitStage.testAllStageUnitInArea(area.transform, this.ownerCharacter, sceneID => {
            return sceneID == this.ownerCharacterSceneID
        })

        if (!resultArray) return

        for (const id of resultArray) {
            UnitStage.invokeRegisteredProcessFunction(this.ownerCharacterSceneID, id, this.motionData.name, this.currentFrame)
        }

        if (resultArray.length > 0) {
            SoundService.play3DSound(area.soundID, this.ownerCharacter, 1, 1.5, { radius: 2000 })
        }
    }

    finish() {

    }
}

export function registeredClient(target: any) {
    if (mw.SystemUtil.isServer()) return
    MotionRegister.registerClientExtends(target.prototype.constructor.name, target.prototype.constructor)
}
